#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>

#include "debug.h"
#include "ab_list.h"


static void init_item_attr(st_ab_list_item *pitem)
{
    if(pitem==NULL)
	return ;
    memset(pitem,0,sizeof(st_ab_list_item));
    pitem->magic = AB_LIST_ITEM_MAGIC;
    pitem->status = AB_ITEM_STATUS_NOT_USED;
}

st_ab_dynamic_list *ab_init_list(unsigned char *list_buf,unsigned int volume)
{
    if(list_buf==NULL||volume<=sizeof(st_ab_dynamic_list))
	return NULL;

    printf("---init ab list size = %lu,item size = %lu,volume = %u\n",sizeof(st_ab_dynamic_list),sizeof(st_ab_list_item),volume);
    memset(list_buf,0,volume);
    st_ab_dynamic_list *ab_list = (st_ab_dynamic_list *)list_buf;
    ab_list->volume = volume - sizeof(st_ab_dynamic_list);
    ab_list->freesize = ab_list->volume;
    ab_list->nread = 0;
    ab_list->phead = (st_ab_list_item*)(ab_list+1);
    ab_list->pread = ab_list->phead;
    ab_list->pwrite = ab_list->phead;
    init_item_attr(ab_list->phead);
    //printf("---ab_list head = %p,read = %p,write = %p\n",ab_list->phead,ab_list->pread,ab_list->pwrite);
    pthread_rwlock_init(&ab_list->rwlock,NULL);
    return ab_list;
}
static void ab_reset_list(st_ab_dynamic_list *list)
{
    if(list==NULL)
	return;
    list->pread = list->pwrite = list->phead;
    list->freesize = list->volume;
    list->nread = 0;
    init_item_attr(list->phead);
}

void ab_exit_list(st_ab_dynamic_list *list)
{
    if(list==NULL)
	return;
    pthread_rwlock_destroy(&list->rwlock);
    memset(list,0,list->volume+sizeof(st_ab_dynamic_list));
}

int ab_read_item_data(st_ab_dynamic_list *list,unsigned char *buf,int len)
{
    if(list==NULL||buf==NULL)
	return -1;

    int buf_size = 0;
    int ret = 0;

    pthread_rwlock_rdlock(&list->rwlock);
    //printf("--read ab_list nread = %d,pread = %p,free = %u\n",list->nread,list->pread,list->freesize);
    if(list->nread==0)
    {// pread == pwrite , it's empty!
	if(list->pread==list->pwrite)
	    ret = 0;
	else
	{
	    SLOG(ERR,"this should not happen,we reset list!!!\n");
	    ab_reset_list(list);
	    ret = -1;
	}
	pthread_rwlock_unlock(&list->rwlock);
	return ret;
    }
    if(list->pread==NULL)
    {
	SLOG(ERR,"pread is null!\n");
	ret = -1;
    }
    else if(list->pread->magic!=AB_LIST_ITEM_MAGIC)
    {
	SLOG(ERR,"item magic incorrect[%04x]!\n",list->pread->magic);
	ret = -1;
    }
    else if(list->pread->status!=AB_ITEM_STATUS_USED)
    {
	SLOG(ERR,"read item status isnot USED!\n");
	ret = -1;
    }
    if(ret < 0)
    {
	SLOG(ERR,"this should not happen,we reset list!!!\n");
	ab_reset_list(list);
	pthread_rwlock_unlock(&list->rwlock);
	return -1;
    }

    buf_size = list->pread->len[0]+list->pread->len[1];
    if(buf_size > len)
    {
	//SLOG(ERR,"item data size[%d] is larger then buffer len[%d]\n",buf_size,len);
	ret = 0;
    }
    else if(list->pread->len[0]>0)
    {
	memcpy(buf,list->pread->pdata[0],list->pread->len[0]);
	if(list->pread->len[1]>0)
	   memcpy(buf+list->pread->len[0],list->pread->pdata[1],list->pread->len[1]);
	ret = buf_size;
    }
    if(list->nread>0) 
	list->nread--;
    struct ab_list_item *ptmp = list->pread;
    if(list->pread->next)
    {
	list->freesize += list->pread->actual_size; 
	list->pread = list->pread->next;
	init_item_attr(ptmp);
    }
    else
    {
	SLOG(ERR,"this should not happen,we reset list!!!\n");
	ab_reset_list(list);
	ret = -1;
    }
    pthread_rwlock_unlock(&list->rwlock);

    return ret;
}

int ab_write_item_data(st_ab_dynamic_list *list,unsigned char *header,int hlen,unsigned char *data,int dlen)
{
    if(list==NULL)
	return -1;
    
    int ret = 0;
    int len = 0;
    unsigned char *buf[2];
    int ulen[2];

    buf[0] = header;
    buf[1] = data;
    ulen[0] = hlen;
    ulen[1] = dlen;
    len = ulen[0]+ulen[1];

    //printf("---write ab_list ulen[0] = %d,ulen[1] = %d,len = %d\n",ulen[0],ulen[1],len);
    pthread_rwlock_wrlock(&list->rwlock);
    //printf("---write ab_list nread = %d,pwrite = %p,free = %dB\n",list->nread,list->pwrite,list->freesize);
    if(list->pwrite==NULL)
    {
	SLOG(ERR,"pwrite is null!\n");
	ret = -1;
    }
    else if(list->pwrite->magic!=AB_LIST_ITEM_MAGIC)
    {
	SLOG(ERR,"item magic incorrect[%04x]!=[%#x]!\n",list->pwrite->magic,AB_LIST_ITEM_MAGIC);
	ret = -1;
    }
    else if(list->pwrite->status!=AB_ITEM_STATUS_NOT_USED)
    {
	SLOG(ERR,"write item status is incorrect!\n");
	ret = -1;
    }
    if(ret < 0)
    {
	SLOG(ERR,"this should not happen,we reset list!!!\n");
	ab_reset_list(list);
	pthread_rwlock_unlock(&list->rwlock);
	return -1;
    }

    if(list->freesize < len+sizeof(st_ab_list_item))
    {// list is full!
	printf("!!!list full,free size[%d] < buf len[%d]\n",list->freesize,len+sizeof(st_ab_list_item));
	ret = -2;
    }
    else
    {
	unsigned char *pnext = NULL;
	unsigned char *ptail = (unsigned char *)list->phead+list->volume;
	list->pwrite->pdata[0] = (unsigned char *)list->pwrite+sizeof(st_ab_list_item);
	if(list->pwrite->pdata[0]+len>ptail)
	{
	    int buf0_cp_len = 0,buf1_cp_len = 0;
	    list->pwrite->len[0] = ptail - list->pwrite->pdata[0];
	    list->pwrite->len[1] = len - list->pwrite->len[0];
	    list->pwrite->pdata[1] = (unsigned char *)list->phead;
	    if(list->pwrite->len[0]>ulen[0])
	    {
		memcpy(list->pwrite->pdata[0],buf[0],ulen[0]);
		buf0_cp_len = ulen[0];
		buf1_cp_len = list->pwrite->len[0]-buf0_cp_len;
		memcpy(list->pwrite->pdata[0]+buf0_cp_len,buf[1],buf1_cp_len);
	    }
	    else
	    {
		memcpy(list->pwrite->pdata[0],buf[0],list->pwrite->len[0]);
		buf0_cp_len = list->pwrite->len[0];
		buf1_cp_len = 0;
	    }
	    ulen[0] = ulen[0] - buf0_cp_len;
	    ulen[1] = ulen[1]- buf1_cp_len;
	    if(ulen[0]>0)
	    {
		memcpy(list->pwrite->pdata[1],buf[0]+buf0_cp_len,ulen[0]);
		memcpy(list->pwrite->pdata[1]+ulen[0],buf[1],ulen[1]);
	    }
	    else
	    {
		memcpy(list->pwrite->pdata[1],buf[1]+buf1_cp_len,ulen[1]);
	    }
	    pnext = list->pwrite->pdata[1]+list->pwrite->len[1];
	}
	else
	{
	    list->pwrite->len[0] = len;
	    list->pwrite->len[1] = 0;
	    memcpy(list->pwrite->pdata[0],buf[0],ulen[0]);
	    memcpy(list->pwrite->pdata[0]+ulen[0],buf[1],ulen[1]);
	    pnext = list->pwrite->pdata[0]+list->pwrite->len[0];
	}

	if(pnext+sizeof(struct ab_list_item) >= ptail)
	    pnext = (unsigned char*)list->phead;
	//printf("--write pdata1:%p,len1:%d,pdata2:%p,len2:%d,pnext:%p,ptail:%p\n",
	//	list->pwrite->pdata[0],list->pwrite->len[0],
	//	list->pwrite->pdata[1],list->pwrite->len[1],pnext,ptail);
	/* set item status */
	int aligned_byte = 0;
	if((int)pnext&0x03)
	{
	    aligned_byte = 4 - ((int)pnext&0x03);
	    pnext += aligned_byte;// consider addr memory align 
	}
	list->pwrite->actual_size = len + sizeof(st_ab_list_item) + aligned_byte;
	list->pwrite->status = AB_ITEM_STATUS_USED;
	list->pwrite->next = (st_ab_list_item *)pnext; /* for read easy */
	list->nread++;
	list->freesize -= list->pwrite->actual_size; 
	list->pwrite = (st_ab_list_item*)pnext;
	if(list->pwrite == list->pread)
	{
	    SLOG(WRN,"we catch read,list is full,wait to read!\n");
	}
	else
	    init_item_attr(list->pwrite); // initialize next write
    }
    
    pthread_rwlock_unlock(&list->rwlock);
    return ret;
}

#if 0
#define G_BUFFER_LEN 2048 
static unsigned char g_buffer[G_BUFFER_LEN];
static int runflag = 1;
struct log_module log_conf = 
{ 	
    .log_path = "ablist.log",
    .max_size = 512*1024,
    .fp = NULL,
    .mutex = PTHREAD_MUTEX_INITIALIZER,
};
struct media_frame_header
{
    unsigned int type; // video:I frame,P frame,audio:Audio frame
    unsigned int len;
    unsigned long long pts;
    time_t fts;// frame timestamp 
};
void sig_handle(int signo)
{
    if(signo == SIGINT)
	runflag = 0;
}

int main(int argc, char *argv[])
{
    int ret = 0,i = 0;
    st_ab_dynamic_list *list = 0;
    struct media_frame_header header;
    unsigned char frame[1024]={0};
    unsigned int frame_len = sizeof(frame)-sizeof(header);

    header.type = 0x11;
    header.len = frame_len;
    header.pts = 0x123456789;

    signal(SIGINT,sig_handle);
    list = ab_init_list(g_buffer,G_BUFFER_LEN);
    if(list != NULL)
    {
	while(runflag&&++i)
	{
	    memset(frame,i%255,frame_len);
	    if(ab_write_item_data(list,(unsigned char*)&header,sizeof(header),frame,frame_len)<0)
	    {
		SLOG(ERR,"ab_write_item_data fail!\n");
		break;
	    }
	    memset(frame,0,sizeof(frame));
	    ret = ab_read_item_data(list,frame,sizeof(frame));
	    SLOG(PRI,"ab_read_item_data ret = %d\n",ret);
	    if(ret < 0)
	    {
	        SLOG(ERR,"ab_read_item_data fail!\n");
	        break;
	    }
	    struct media_frame_header *ptmp = (struct media_frame_header *)frame;
	    unsigned char *ptmp2 = frame+sizeof(header);
	    ret = ret-sizeof(header);
	    printf("---header type:%02x,len:%u,pts:%#llx\n",ptmp->type,ptmp->len,ptmp->pts);
	    //unsigned char *ptmp2 = frame;
	    printf("---ptmp2[%d]:%d %d %d ... %d %d %d\n\n",i,ptmp2[0],ptmp2[1],
		    ptmp2[2],ptmp2[ret-3],ptmp2[ret-2],ptmp2[ret-1]);
	    usleep(100000);
	}
	ab_exit_list(list);	
    }
    else
    {
	SLOG(ERR,"list is null!\n");
	exit(EXIT_FAILURE);
    }

    return ret;
}
#endif

